from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.remote.errorhandler import ElementClickInterceptedException
from selenium.webdriver.remote.errorhandler import ElementNotInteractableException
from selenium.webdriver.remote.errorhandler import WebDriverException
from selenium.common.exceptions import NoAlertPresentException
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from time import sleep
import time
import re
import pathlib
from ..globals import g
from ..elements import e
from ..windows import w
from ..locator import locating_elements, locating_data, locating_element
from ..log import logger, snapshot_post
from ..parse import data_format
from ..utility import compare, json2dict, log_default_warning, get_keyword_param
from .. import config


# class Common():
#     @classmethod
#     def title(cls, data, output):
#         logger.info('DATA:%s' % repr(data['text']))
#         logger.info('REAL:%s' % repr(g.driver.title))
#         if data['text'].startswith('*'):
#             assert data['text'][1:] in g.driver.title
#         else:
#             assert data['text'] == g.driver.title
#         # 只能获取到元素标题
#         for key in output:
#             g.var[key] = g.driver.title
#
#     @classmethod
#     def current_url(cls, data, output):
#         logger.info('DATA:%s' % repr(data['text']))
#         logger.info('REAL:%s' % repr(g.driver.current_url))
#         if data['text'].startswith('*'):
#             assert data['text'][1:] in g.driver.current_url
#         else:
#             assert data['text'] == g.driver.current_url
#         # 只能获取到元素 url
#         for key in output:
#             g.var[key] = g.driver.current_url


def select(step):
    data: dict = step['data']
    element_info = step['element']
    option_index = data.get('option_index')
    option_value = data.get('option_value')
    option_text = data.get('option_text')

    if not (option_index or option_value or option_text):
        raise AssertionError('关键字参数<option_index><option_value><option_text>必须填写一项！')

    element_location = locating_element(element_info, action='CLICK')
    s = Select(element_location)

    for k, v in data.items():
        if k == 'option_index':
            s.select_by_index(int(v)-1)
            break
        elif k == 'option_value':
            s.select_by_value(v)
            break
        elif k == 'option_text':
            s.select_by_visible_text(v)
            break


def open(step):
    element = step['element']
    if element:
        value = e.get(element)[1]
    else:
        value = step['data']['text']
    if step['data'].get('清理缓存', '') or step['data'].get('clear', ''):
        g.driver.delete_all_cookies()
    if step['data'].get('打开方式', '') == '新标签页' or step['data'].get('mode', '').lower() == 'tab':
        js = "window.open('%s')" % value
        g.driver.execute_script(js)
        # 判断是否打开了新的窗口，并将新窗口添加到所有窗口列表里
        # all_handles = g.driver.window_handles
        # for handle in all_handles:
        #     if handle not in w.windows.values():
        #         w.register(step, handle)
    else:
        if step['data'].get('打开方式', '') == '新浏览器' or step['data'].get('mode', '').lower() == 'browser':
            w.close()
            g.set_driver()
            w.init()
        g.driver.get(value)
        # w.open(step)
    cookie = step['data'].get('cookie', '')
    if cookie:
        g.driver.add_cookie(json2dict(cookie))
        co = g.driver.get_cookie(json2dict(cookie).get('name', ''))
        logger.info(f'cookie is add: {co}')
    sleep(0.5)


def check(step):
    expect = step['expected']
    data = step['data']
    element = step['element']
    output: dict = step['output']

    udef_wait_time = data.get('等待时间')
    if udef_wait_time:
        wait_true_time = float(udef_wait_time)
    else:
        wait_true_time = g.element_wait_timeout

    wait_for_check = data.get('是否等待直到结果为真')
    is_visible = data.get('元素是否可见')

    if is_visible == '是':
        action = ''
    elif is_visible == '否':
        action = 'not_visible'
    else:
        action = ''
        log_default_warning(keyword='检查', param='元素是否可见', default_value='是')
    if not wait_for_check:
        wait_for_check = '是'
        log_default_warning(keyword='检查', param='是否等待直到结果为真', default_value=wait_for_check)

    element_location = locating_element(element, action=action, wait_time=wait_true_time) if element else None
    if not element and 'current_url' not in expect:
        logger.warning('未选择元素！')

    t = time.time()
    while True:
        try:
            for key in expect:
                expected = expect[key]
                if key == 'title' and not element:
                    real = g.driver.title
                elif key == 'current_url':
                    real = g.driver.current_url
                else:
                    if key == 'text':
                        real = element_location.text
                    else:
                        real = element_location.get_attribute(key)
                g.logger.info(f'预期结果: "{expected}"，当前值: "{real}"')
                compare(expected, real)
            break
        except:
            if wait_for_check == '否' or time.time() - t > wait_true_time:
                try:
                    logger.info(f'预期结果校验失败，预期结果: "{expected}"，当前值: "{real}"')
                except:
                    pass
                raise
            time.sleep(0.1)
            if element:
                element_location = locating_element(element, action=action, wait_time=wait_true_time)

    allow_result_empty = get_keyword_param(data, '输出结果是否可以为空', '否')
    t = time.time()
    while True:
        value_is_empty = False
        for key, v in output.items():
            if element_location:
                element_text = element_location.text
                element_attr = element_location.get_attribute(output[key])
                if not element_text:
                    value_is_empty = True
                if v == 'text':
                    g.update_var(key=key, value=element_text)
                elif v in ('text…', 'text...'):
                    if v.endswith('...'):
                        g.update_var(key=key, value=element_text[:-3])
                    else:
                        g.update_var(key=key, value=element_text)
                else:
                    g.update_var(key=key, value=element_attr)
            elif v == 'title':
                g.update_var(key=key, value=g.driver.title)
            elif v == 'current_url':
                g.update_var(key=key, value=g.driver.current_url)
        if value_is_empty or allow_result_empty == '否':
            break
        else:
            if time.time() - t > g.element_wait_timeout:
                logger.exception(f'元素属性值为空，输出结果检查 {g.element_wait_timeout} 超时！')
                raise Exception('元素属性值为空！')
            else:
                time.sleep(0.1)


    return element_location


def input(step):
    data: dict = step['data']
    element = step['element']

    element_location = locating_element(element)

    if step['data'].get('是否清除文本', '') == '否' or step['data'].get('clear', '').lower() == 'no':
        pass
    else:
        element_location.clear()

    if_v_key = True if data.get('是否使用模拟按键') == '是' else False

    for key, s in data.items():
        if key == 'text':
            if if_v_key:
                matchs = re.findall('<.*?>', s)
                for k in matchs:
                    ctrl_key = getattr(Keys, k[1:-1].upper(), None)
                    if ctrl_key:
                        s = s.replace(k, ctrl_key)
            element_location.send_keys(s)

    return element_location


def clear(step):
    element = step['element']
    element_location = locating_element(element)
    element_location.clear()


def click(step):
    element = step['element']
    data = step['data']
    el_wait_time = float(data.get('等待时间', g.element_wait_timeout))
    is_visible = data.get('元素是否可见')
    action = 'not_visible' if is_visible == '否' else 'CLICK'

    element_location = locating_element(element, action=action, wait_time=el_wait_time)
    if element_location:
        end_time = time.time() + g.element_wait_timeout
        while True:
            try:
                element_location.click()
                break
            except ElementNotInteractableException or ElementClickInterceptedException:
                if time.time() > end_time:
                    raise
                sleep(0.5)
            except WebDriverException as e:
                if time.time() > end_time:
                    raise
                s = repr(e)
                if 'is not clickable' in s:
                    sleep(0.5)
                else:
                    raise

    return element_location


def close_current_window(step):
    g.driver.close()


def switch_window(step):
    data = step['data']
    use_re = data.get('是否使用正则表达式')
    if use_re == '是':
        is_use_re = True
    elif use_re == '否':
        is_use_re = False
    else:
        is_use_re = False
        log_default_warning(keyword='切换浏览器窗口', param='是否使用正则表达式', default_value='否')
    t = time.time()
    while True:
        try:
            for atr in data:
                atr_value = data[atr]
                if atr == 'title':
                    for handle in g.driver.window_handles:
                        g.driver.switch_to_window(handle)
                        if is_use_re:
                            if re.search(atr_value, g.driver.title):
                                return
                        else:
                            if g.driver.title == atr_value:
                                return
                elif atr == 'url':
                    tar_url: str = atr_value
                    for handle in g.driver.window_handles:
                        g.driver.switch_to_window(handle)
                        if is_use_re:
                            if re.search(atr_value, g.driver.current_url):
                                return
                        else:
                            if g.driver.current_url == tar_url:
                                return
                elif atr == 'index':
                    g.driver.switch_to_window(g.driver.window_handles[int(atr_value)])
                    return
            raise Exception(f'未找到指定窗口')
        except:
            if time.time() - t > g.element_wait_timeout:
                raise
                # raise Exception(f'未找到指定窗口: {data}')
            else:
                sleep(1)


def move(step):
    actions = ActionChains(g.driver)
    element = step['element']
    element_location = locating_element(element)
    actions.move_to_element(element_location)
    actions.perform()
    sleep(0.5)

    return element_location


def context_click(step):
    actions = ActionChains(g.driver)
    element = step['element']
    element_location = locating_element(element)
    actions.context_click(element_location)
    actions.perform()
    sleep(0.5)

    return element_location


def double_click(step):
    actions = ActionChains(g.driver)
    element = step['element']
    element_location = locating_element(element)
    actions.double_click(element_location)
    actions.perform()
    sleep(0.5)

    return element_location


def drag_and_drop(step):
    actions = ActionChains(g.driver)
    element = step['element']
    if element:
        source = locating_element(element[0])
        target = locating_element(element[1])
        actions.drag_and_drop(source, target)
        actions.perform()
    else:
        data = step['data']
        source = locating_element(element)
        x = data.get('x', 0)
        y = data.get('y', 0)
        actions.drag_and_drop_by_offset(source, x, y)
        actions.perform()
    sleep(0.5)


def script(step):
    data = step['data']
    expect = step['expected']
    output = step['output']
    js = data['js']
    t = data.get('等待时间')
    if t:
        wait_true_time = float(t)
    else:
        wait_true_time = g.element_wait_timeout
    allow_result_empty = get_keyword_param(data, '输出结果是否可以为空', '是')

    def get_js_result():
        end_t = time.time() + wait_true_time
        while True:
            rsp = g.driver.execute_script(js)
            s = '' if not rsp else rsp
            if s or allow_result_empty == '是':
                return s
            if time.time() > end_t:
                raise TimeoutError(f'获取js返回结果{wait_true_time}超时！')
            time.sleep(0.3)

    js_result = get_js_result()
    end_t = time.time() + wait_true_time
    while True:
        try:
            for key in expect:
                expected = expect[key]
                if key == 'text':
                    compare(expected, js_result)
                logger.info(f'预期结果: "{expected}"，当前值: "{js_result}"')
            break
        except:
            if time.time() > end_t:
                raise TimeoutError(f'预期结果校验失败，预期结果: "{expected}"，当前值: "{js_result}"')
            time.sleep(0.2)
            js_result = get_js_result()

    for k, v in output.items():
        if v == 'text':
            g.update_var(k, js_result)


def alert_box(step):
    data = step['data']
    output = step['output']
    text = data.get('text', '')
    opt = data.get('操作')
    wait_time = float(get_keyword_param(data, '等待时间', g.element_wait_timeout))


    t = time.time()
    while True:
        try:
            alert = g.driver.switch_to_alert()
            alert_text = alert.text
            if opt in ('确定', '确认'):
                alert.accept()
            elif opt == '关闭':
                alert.dismiss()
            elif opt == '输入':
                alert.send_keys(text)
                alert.accept()
            break
        except NoAlertPresentException:
            if time.time() - t > wait_time:
                raise Exception(f'查找对话框{g.element_wait_timeout}s超时，未发现对话框！')
            sleep(0.3)

    for k, v in output.items():
        if v == 'text':
            g.update_var(key=k, value=alert_text)


def upload(step):
    data = step['data']
    element = step['element']
    file_path = data.get('text', '') or data.get('file', '')
    is_visible = data.get('元素是否可见')
    if is_visible == '是':
        action = ''
    elif is_visible == '否':
        action = 'not_visible'
    else:
        action = ''
        log_default_warning(keyword='上传文件', param='元素是否可见', default_value='是')

    element_location = locating_element(element, action=action)
    if element_location.get_attribute('type') != 'file':
        raise Exception('元素选择有误，该元素不是file类型！')
    else:
        # if element_location.get_attribute('readonly'):
        element_location.send_keys(file_path)


def navigate(step):
    element = step['element']

    if element.lower() in ('刷新', 'refresh'):
        g.driver.refresh()
    elif element.lower() in ('前进', 'forward'):
        g.driver.forward()
    elif element.lower() in ('后退', 'back'):
        g.driver.back()


def refresh(step):
    g.driver.refresh()
    w.frame = 0


def forward(step):
    g.driver.forward()


def back(step):
    g.driver.back()


def scroll(step):
    data = step['data']
    x = data.get('横向距离')
    y = data.get('纵向距离')

    element = step['element']
    if element == '':
        # if x is None:
        #     x = '0'
        # g.driver.execute_script(
        #     f"window.scrollTo({x},{y})")
        if y:
            g.driver.execute_script(
                f"document.documentElement.scrollTop={y}")
        if x:
            g.driver.execute_script(
                f"document.documentElement.scrollLeft={x}")         
    else:
        element_location = locating_element(element)
        g.driver.execute_script('arguments[0].scrollIntoView();', element_location)


def screenshot(step: dict):
    data: dict = step['data']
    shot_name = data.get('截图名称', '截图')
    file_name = f"{g.task_id}_{g.result_case_id}_{int(time.time())}.png"
    step['snapshot'] = str(pathlib.Path(config.temp_dir, file_name))
    g.driver.switch_to.default_content()
    try:
        g.driver.get_screenshot_as_file(step['snapshot'])
    except:
        logger.error('截图失败！')
        raise
    # ------- 上传报错时截图 -------
    snapshot_file = step.get('snapshot')
    if snapshot_file:
        snapshot_post(snapshot_file, file_name=shot_name, file_name_post=file_name)
